package com.biscuit.page.basicservice.conf.aop;

import com.biscuit.page.basicservice.conf.handler.TransactionHandler;
import com.biscuit.page.basicservice.utils.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.annotation.Resource;

/**
 * @author mazihao
 * @createTime 2024/3/30 10:36
 */
@Slf4j
@Aspect
@Component
public class TransactionAspect {

    @Resource
    private DataSourceTransactionManager transactionManager;

    @Pointcut("execution(public * com.biscuit.page.basicservice.service.*.exec*(..)) " +
            "|| execution(public * com.biscuit.page.basicservice.service.*.del*(..)) " +
            "|| execution(public * com.biscuit.page.basicservice.service.*.save*(..)) " +
            "|| execution(public * com.biscuit.page.basicservice.service.*.update*(..))")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object transactionAround(ProceedingJoinPoint point) throws Throwable {
        // 判断是否开启了事务
        if (TransactionHandler.isOpenTx()) {
            // 已经开启过事务时，说明是事务方法内的调用，则不重新开启事务
            log.info("事务方法的内部调用，无处理");
            return point.proceed();
        }

        // 获取目标方法所在类的类名
        String className = point.getSignature().getDeclaringTypeName();

        // 获取目标方法的方法名
        String methodName = point.getSignature().getName();

        // 组装事务id
        String txId = "tx_" + className + "_" + methodName + "_" + System.currentTimeMillis();

        // 设置到线程变量中
        TransactionHandler.setTxId(txId);
        log.info("txId => {}", txId);

        // 开启事务
        TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            // 执行业务逻辑
            Object resultObj = point.proceed();
            // 提交事务
            transactionManager.commit(transaction);
            return resultObj;
        } catch (Throwable ex) {
            // 回滚事务
            log.error("{} {} - {} transaction，事务回滚", point.getSignature().getDeclaringTypeName(), point.getSignature().getName(), DateUtil.getCurrentDateTime());
            transactionManager.rollback(transaction);
            throw ex;
        } finally {
            // 清除 ThreadLocal 避免内存泄漏
            TransactionHandler.clear();
        }
    }

}
